package service

import (
	"GopherFocus/app/dao"
	"GopherFocus/app/model"
	"GopherFocus/app/shared"
	"context"
	"github.com/gogf/gf/database/gdb"
	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/frame/g"
)

var Interact = interactService{}

type interactService struct{}

const (
	contextMapKeyForInteractList = "contextMapKeyForMyInteractList"
)

// 赞
// r.RowsAffected()
// r.LastInsertId()
func (s *interactService) Zan(ctx context.Context, targetId uint, targetType string) (err error) {
	return dao.Interact.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		customCtx := shared.Context.Get(ctx)
		// 为什么要加上这一步????
		if customCtx == nil || customCtx.User == nil {
			return nil
		}
		// gf_interact表设置了唯一主键: unique key `unique` (`user_id`,`target_id`,`target_type`,`type`) using btree
		// 所以当数据库中已存在一条唯一主键相同的数据时，是无法继续插入的
		//
		// InsertIgnore相当于 insert ignore into xxx 表示如果数据库中不存在重复记录，则插入，否则跳过
		// 为什么这里要加FieldsEx() ???
		r, err := dao.Interact.Ctx(ctx).Data(g.Map{
			dao.Interact.C.UserId:     customCtx.User.Id,
			dao.Interact.C.TargetId:   targetId,
			dao.Interact.C.TargetType: targetType,
			dao.Interact.C.Type:       model.InteractTypeZan,
		}).FieldsEx(dao.Content.C.Id).InsertIgnore()

		if err != nil {
			return err
		}
		// r.RowsAffected() 返回被修改的记录数
		if n, _ := r.RowsAffected(); n == 0 {
			return gerror.Newf(`您已赞过啦`)
		}
		return s.updateCount(ctx, model.InteractTypeZan, targetType, targetId, 1)
	})
}

// 根据业务类型（interactType）指定踩/赞的数量
// interactType 指定是进行点赞还是进行踩; targetType指定进行踩或赞的内容类型（内容或回复）
// Update() Insertxxx() Increment()返回两个参数，一个参数是result，一个error
func (s *interactService) updateCount(ctx context.Context, interactType int, targetType string, targetId uint, count int) error {
	return dao.Interact.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 为什么要这样设计
		defer func() {
			// 清空上下文对应的互动数据缓存
			if customCtx := shared.Context.Get(ctx); customCtx != nil {
				delete(customCtx.Data, contextMapKeyForInteractList)
			}
		}()

		var err error
		switch targetType {
		// 对内容进行赞踩
		case model.InteractTargetTypeContent:
			// 选取赞的数量为0的数据，然后将赞的数量+1
			// 如果赞的数量为0的数据的总数不为0，则表示该内容已被点赞过，无法继续点赞
			// Where(dao.Content.C.Id, targetId)用于选择内容
			switch interactType {
			case model.InteractTypeZan:
				_, err = dao.Content.Ctx(ctx).
					Where(dao.Content.C.ZanCount, 0).
					Where(dao.Content.C.Id, targetId).
					Increment(dao.Content.C.ZanCount, 1)
				if err != nil {
					return err
				}
			// 对内容进行踩
			case model.InteractTypeCai:
				//
				_, err = dao.Content.Ctx(ctx).
					Where(dao.Content.C.CaiCount, 0).
					Where(dao.Content.C.Id, targetId).
					Increment(dao.Content.C.CaiCount, 1)
				if err != nil {
					return err
				}
			}
		// 对回复进行踩赞
		case model.InteractTargetTypeReply:
			switch interactType {
			case model.InteractTypeZan:
				_, err = dao.Reply.Ctx(ctx).
					Where(dao.Reply.C.ZanCount, 0).
					Where(dao.Reply.C.Id, targetId).
					Increment(dao.Reply.C.ZanCount, 1)
				if err != nil {
					return err
				}
			case model.InteractTypeCai:
				_, err = dao.Reply.Ctx(ctx).
					Where(dao.Reply.C.CaiCount, 0).
					Where(dao.Reply.C.Id, targetId).
					Increment(dao.Reply.C.CaiCount, 1)
				if err != nil {
					return err
				}
			}
		}
		return nil
	})
}

// 取消赞
func (s *interactService) CancelZan(ctx context.Context, id uint, targetType string) error {
	return dao.Interact.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 为什么要这样???
		customCtx := shared.Context.Get(ctx)
		if customCtx == nil || customCtx.User == nil {
			return nil
		}
		// 删除赞的交互记录
		r, err := dao.Interact.Ctx(ctx).Where(g.Map{
			dao.Interact.C.UserId:     customCtx.User.Id,
			dao.Interact.C.TargetId:   id,
			dao.Interact.C.TargetType: targetType,
			dao.Interact.C.Type:       model.InteractTypeZan,
		}).Delete()
		if err != nil {
			return err
		}
		if n, _ := r.RowsAffected(); n == 0 {
			return nil
		}
		return s.updateCount(ctx, model.InteractTypeZan, targetType, id, -1)
	})
}

// 踩
// TargetType对应ContentType
// id对应targetId
func (s *interactService) Cai(ctx context.Context, id uint, targetType string) error {
	return dao.Interact.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		customCtx := shared.Context.Get(ctx)
		if customCtx == nil || customCtx.User == nil {
			return nil
		}
		// 添加踩的交互记录
		// dao.Interact.C.Type 表示赞或踩等交互类型
		// 为什么这里加FieldsEx(dao.Interact.C.Id)
		r, err := dao.Interact.Ctx(ctx).Data(g.Map{
			dao.Interact.C.UserId:     customCtx.User.Id,
			dao.Interact.C.TargetId:   id,
			dao.Interact.C.TargetType: targetType,
			dao.Interact.C.Type:       model.InteractTypeCai,
		}).FieldsEx(dao.Interact.C.Id).InsertIgnore()
		if err != nil {
			return err
		}
		if n, _ := r.RowsAffected(); n == 0 {
			return gerror.Newf(`您已经踩过`)
		}
		return s.updateCount(ctx, model.InteractTypeCai, targetType, id, 1)
	})
}

// 取消踩
func (s *interactService) CancelCai(ctx context.Context, id uint, targetType string) error {
	return dao.Interact.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		customCtx := shared.Context.Get(ctx)
		if customCtx == nil || customCtx.User == nil {
			return nil
		}
		r, err := dao.Interact.Ctx(ctx).Where(g.Map{
			dao.Interact.C.UserId:     customCtx.User.Id,
			dao.Interact.C.TargetId:   id,
			dao.Interact.C.TargetType: targetType,
			dao.Interact.C.Type:       model.InteractTypeCai,
		}).Delete()
		if err != nil {
			return err
		}
		// 需要判断影响了多少数据，如果影响了0条，说明没有踩过，则不需要取消踩
		if n, _ := r.RowsAffected(); n == 0 {
			return nil
		}
		return s.updateCount(ctx, model.InteractTypeCai, targetType, id, -1)
	})
}
